package org.mat.framework.logging.operation.support;

import org.mat.framework.lang.constant.DefaultErrors;
import org.mat.framework.lang.exception.BusinessException;
import org.mat.framework.logging.operation.api.ApiLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * <p>Title: OperationLoggingAspect </p>
 * <p>Date: 2020/4/22 </p>
 * <p>Description: </p>
 *
 * @author sunxinhe
 */
@Aspect
@Slf4j
public class OperationLoggingAspect {

    @Autowired
    OperationLoggingExecutor operationLoggingExecutor;

    /**
     * 匹配带有自定义注解的方法
     */
    @Pointcut("@annotation(org.mat.framework.logging.operation.api.ApiLog)")
    public void pointcut() {
    }


    /**
     * 第一时间处理业务，并透传业务异常
     * 日志单独处理（默认异步），且日志处理过程异常不向外抛出，打印系统日志以备排查
     * 无论业务是否成功，日志都需要记录
     *
     * @param proceedingJoinPoint 连接点对象
     * @return
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 记录开始时间
        LocalDateTime beginTime = LocalDateTime.now();

        // 记录异常信息
        String errorCode = null;

        // 处理业务，并透传业务异常
        Object result = null;
        try {
            result = proceedingJoinPoint.proceed();
        } catch (BusinessException businessException) {
            // 记录业务异常
            errorCode = businessException.getCode();
            throw businessException;
        } catch (Throwable throwable) {
            // 记录系统异常
            errorCode = DefaultErrors.DEFAULT.getCode();
            throw throwable;
        } finally {

            // 记录日志
            saveLog(proceedingJoinPoint, beginTime, errorCode);
        }

        return result;
    }

    /**
     * 日志单独处理（默认异步），且日志处理过程异常不向外抛出，打印系统日志以备排查
     *
     * @param proceedingJoinPoint 连接点对象
     * @param beginTime           开始时间
     * @param errorCode           异常码
     */
    private void saveLog(ProceedingJoinPoint proceedingJoinPoint, LocalDateTime beginTime, String errorCode) {
        try {
            // 获取基础参数
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();

            Object[] args = proceedingJoinPoint.getArgs();

            MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
            Method method = signature.getMethod();
            ApiLog apiLog = method.getAnnotation(ApiLog.class);

            // 发送操作日志
            operationLoggingExecutor.saveLog(httpServletRequest, apiLog, args, beginTime, errorCode);

        } catch (Throwable throwable) {
            log.error("发送日志发生异常", throwable);
        }
    }

}
